package com.luis.toolsuite.downsampling.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.math3.stat.regression.RegressionResults;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.springframework.stereotype.Service;

import com.luis.toolsuite.downsampling.model.DataPoint;
import com.luis.toolsuite.downsampling.service.DownsampleStrategy;
import com.luis.toolsuite.util.Helper;

/**largest triangle dynamic*/
@Service("LTD")
public class LTDStrategy implements DownsampleStrategy{

	@Override
	public List<DataPoint> downsample(List<DataPoint> original) {
		//step 1 初始化分组
		List<List<DataPoint>> bucketList = Helper.split(original);
		//真正处理的桶是去头去尾的
		List<List<DataPoint>> tempBucketList = new ArrayList<>(bucketList.subList(1, bucketList.size()-1));
		int times = Helper.TOTAL_SIZE / Helper.BUCKET * 10;
		for(int i= 0;i<times;i++) {
			//循环分配,实践中在中间的某个阶段总的评分会收敛到两个值而互相转换
			tempBucketList = bucketArrange(original.get(0),tempBucketList,original.get(original.size()-1));
		}
		//组装最终用于LTTB计算的bucket list
		List<List<DataPoint>> finalBucketList = new ArrayList<>();
		finalBucketList.add(bucketList.get(0));
		finalBucketList.addAll(tempBucketList);
		finalBucketList.add(bucketList.get(bucketList.size()-1));
		
		//应用LTTB算法
		List<DataPoint> result = new ArrayList<>();
		result.add(original.get(0));
		DataPoint firstPoint = original.get(0);
		for(int i = 2 ;i < finalBucketList.size();i++) {
			List<DataPoint> midBucket = finalBucketList.get(i-1);
			DataPoint lastPoint = Helper.getMedianPoint(finalBucketList.get(i));//后一个点是后一个bucket的中位点
			DataPoint midPoint = Helper.getLargetTriangelPoint(firstPoint,lastPoint,midBucket);
			result.add(midPoint);
			firstPoint = midPoint;
		}
		result.add(original.get(original.size()-1));
		return result;
	}
	
	private List<List<DataPoint>> bucketArrange(DataPoint first,List<List<DataPoint>> tempBucketList, DataPoint last){
		int tempSize = tempBucketList.size();
		double[] rank = new double[tempSize];
		for(int i=0;i<tempSize; i++) {
			DataPoint pre,next;
			if(i==0) {
				pre = first;
			}else {
				//前一个桶的最后一个点
				pre = tempBucketList.get(i-1).get(tempBucketList.get(i-1).size()-1);
			}
			if(i==tempSize-1) {
				next = last;
			}else {
				//后一个桶的第一个点
				next= tempBucketList.get(i+1).get(0);
			}
			//计算桶得分
			rank[i] = calcBucketRank(pre,tempBucketList.get(i), next);
		}
		//获取最大桶和最小桶的位置
		int[] index = peekBucket(rank);
		List<List<DataPoint>> resultList = new ArrayList<>();
		//重新分配
		for(int i=0;i<tempSize; i++) {
			List<DataPoint> tempBucket = new ArrayList<>(tempBucketList.get(i));
			if(i == index[0]) {
				//最大桶则分裂
				int totalSize = tempBucket.size();
				int splittor = (int)totalSize/2;
				resultList.add(tempBucket.subList(0, splittor));
				resultList.add(tempBucket.subList(splittor, totalSize));
			}else if(i == index[1]) {
				tempBucket.addAll(tempBucketList.get(i+1));
				resultList.add(tempBucket);
				i++;//跳过下一个被合并的列表
			}else {
				resultList.add(tempBucket);
			}
		}
		return resultList;
	}
	
	//计算每个桶的SSE作为评分的依据
	private double calcBucketRank(DataPoint pre, List<DataPoint> bucket, DataPoint next) {
		//计算线性回归
		SimpleRegression sg = new SimpleRegression();
		sg.addData(pre.getTime(), pre.getScore());
		for(DataPoint dp : bucket) {
			sg.addData(dp.getTime(), dp.getScore());
		}
		sg.addData(next.getTime(), next.getScore());
		RegressionResults result = sg.regress();
		//返回线性回归的SSE
		return result.getErrorSumSquares();		
	}
	//遍历桶的得分，获取最大bucket的index和最小bucket pair的index
	private int[] peekBucket(double[] rank) {
		int maxIndex = 0,minIndex=0;
		double tempMax = rank[0],tempMin=rank[0]+rank[1];
		for(int i=1;i< rank.length;i++) {
			if(rank[i] > tempMax) {
				tempMax = rank[i];
				maxIndex = i;
			}			
		}
		//确保最小pair中不含最大桶
		for(int i=1;i< rank.length;i++) {
			if((rank[i-1] + rank[i] < tempMin) && i !=maxIndex && i !=(maxIndex+1)) {
				tempMin = rank[i-1] + rank[i];
				minIndex = i-1;
			}
		}
		return new int[] {maxIndex,minIndex};
	}
	
	
	public static void main(String args[]) {
		List<Integer> test = Arrays.asList(1,2,3,4,5);
		int size = test.size();
		List<List<Integer>> result = new ArrayList<>();
		result.add(test.subList(0, (int)size/2));
		result.add(test.subList((int)size/2,size));
		System.out.println(result);
	}
	

}
